﻿open Lambda

type SlotValue =
    | Constant of int
    | Succ
    | Double
    | Get
    | Put
    | S
    | K
    | I
    | Inc
    | Dec
    | Attack
    | Help
    | Copy
    | Revive
    | Zombie
    | App of SlotValue * SlotValue

type Slot =
    {   Vitality:   int;
        LastUpdate: int;
        Value:  SlotValue;    }

let defaultSlot =
    {   Vitality = 10000;
        LastUpdate = 0;
        Value = I;  }

let my = Array.init 256 (fun _ -> defaultSlot)
let enemy = Array.init 256 (fun _ -> defaultSlot)
let locks = Array.zeroCreate 256

let update slot newValue turn =
    { slot with
        LastUpdate = turn;
        Value = newValue;   }

let killerAllocSequence = [128; 129; 130; 131; 132; 133; 134; 64; 65; 66; 32; 33; 34]
let deathWarning = 2000
let killers = ref []

let inline fand f1 f2 x = f1 x && f2 x
let notImplemented() = System.NotImplementedException() |> raise

open System.Collections.Generic

let rec complexity num =
    match num with
    | 0 -> 1
    | n when n &&& 1 = 0 -> 1 + complexity(n / 2)
    | n -> 1 + complexity(n - 1)

let comparerBy complexity =
    { new IComparer<_> with
        member this.Compare(a, b) =
            compare (complexity a) (complexity b)   }

let alloc = LinkedList([0 .. 255] |> List.sortBy complexity)
// do  for i = 0 to 255 do alloc.Add(i, null)
let inline alive slot = my.[slot].Vitality > 0
let inline vitality slot = my.[slot].Vitality
let inline healthy slot = my.[slot].Vitality >= deathWarning
let inline free slot = not locks.[slot]
let inline lock slot =
    if locks.[slot] then invalidOp "slot is already locked"
    locks.[slot] <- true

let allocFast() = notImplemented()

let ensureKiller() =
    // remove dead corpses
    killers := List.filter (fun slot -> my.[slot].Vitality > 0) !killers
    match List.tryFind (fun slot -> my.[slot].Vitality >= deathWarning) !killers with
    | Some(slot) ->
        // there is totally alive killer
        slot
    | None ->
        // if there exists alive killer, use it
        let slot =
            if List.length !killers > 0 then
                Some(List.head !killers)
            else None
        let candidates = killerAllocSequence |> List.filter (fand healthy free)
        let killer =
            if candidates <> [] then
                let killer = List.head candidates
                lock killer
                killer
            else allocFast()
        killers := killer :: !killers
        defaultArg slot killer

let megaHeal() =
    ()

let killThemAll() =
    while vitality(ensureKiller()) < 50000 do
        //megaHeal
        ()
    //megaKill
    ()



//let rec compile value =
//    let existing = getExisting value |> Seq.tryFind (fun (i, mine, locked) -> mine && not locked)
//    // TODO: фиксирование
//    match existing with
//    | Some(i, mine, locked) -> 0, Constant(i)
//    | None ->
//    match value with
//    | Constant(0) -> 1, Constant(0)
//    | Constant(i) ->
//        if i <= 0 || i >= 65536 then failwith "invalid constant"
//        let predCost, pred = compile(Constant (i - 1))
//        let predCost, pred =
//            if i &&& 1 = 0 then
//                let halfCost, half = compile(Constant(i / 2))
//                if halfCost < predCost then halfCost, half else predCost, pred
//            else
//                predCost, pred
//        predCost + 1, pred
//    | App(func, target) ->
//        let funcCost, funcOp = compile func
//        let targetCost, targetOp = compile target

let I x = x
let zero = 0
let succ = function Constant(x) -> Constant(x + 1)
let dbl = function Constant(x) -> Constant(x * 2)
let get = function Constant(x) -> my.[x].Value
let put x = I
let S f g x = (f x) (g x)
let K x y = x
let inc = function Constant(i) ->
                    my.[i] <- { my.[i] with Vitality = my.[i].Vitality + 1 }
                    I

let dec = function Constant(i) ->
                    enemy.[255 - i] <- { enemy.[255 - i] with Vitality = enemy.[255 - i].Vitality - 1 }
                    I

let attack i j n =
    I

let help i j n =
    I

let heali i = help i i

let copy = function Constant(i) -> enemy.[i].Value
let revive = function Constant(i) ->
                        my.[i] <- { my.[i] with Vitality = 1 }
                        I
let zombie = function Constant(i) ->
                        fun x ->
//                            enemy.[255 - i] <- {    Vitality = -1;
//                                                    Value = x;  }
                            I

let BAct a = S (K S) K a // <- 5
let B f g x = f (g x)
let C x = S (B B S) (K K) x

// let YAct f = S S K (S (K (S S (S (S S K)))) K) f <- 12
let rec Y f x = f (Y f) x



let loopSX loop f = (B (loop f) (B succ f))

// (B (loop f) (B succ f))

// let loopF x = Y loopS x
let rec loop f start =
    loop f (succ(f start))

do
    CardFunctions.test()
    let callCombinator = asCombinator CardFunctions.call
    printfn "apply: %O" (asCombinator CardFunctions.apply)
    printfn "call: %O" callCombinator
    printfn "loop: %O" (asCombinator CardFunctions.loop)
    printfn "infloop: %O" (asCombinator CardFunctions.infloop)
    printfn "helpiii: %O" (asCombinator CardFunctions.helpiii)
    printfn "helpiin: %O" (asCombinator CardFunctions.helpiin)
    printfn "done."
(*
infinite loop:
0: (S get id) zero = (get zero) (id zero)

B loop (B succ (B f) start)

let something f x =
    ....
    x

128 <- 9, любой слот
attack 128 j <- 9 + 1 + count(j) + 1


B f g x = f (g y) <- 4

0 <- 1
1 <- 2

*)